package org.systemsbiology.rface.rest;

import org.json.JSONArray;
import org.json.JSONObject;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.systemsbiology.addama.commons.web.views.JsonItemsView;
import org.systemsbiology.rface.pojos.RFACEBean;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.logging.Logger;
import java.util.Comparator;

import org.neo4j.graphdb.index.*;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.traversal.*;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.neo4j.kernel.Traversal;


import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;



/**
 * Created by IntelliJ IDEA.
 * User: aeakin
 * Date: Jul 26, 2011
 * Time: 10:21:40 AM
 * To change this template use File | Settings | File Templates.
 */
public class RFACEServiceController extends AbstractController implements InitializingBean {
    private static final Logger log = Logger.getLogger(RFACEServiceController.class.getName());
    private GraphDatabaseService graphDB;

      enum MyRelationshipTypes implements RelationshipType
 {
     RFACE
 }
    
    public void afterPropertiesSet() throws Exception {
       
    }

    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String requestUri = request.getRequestURI();
        log.info(requestUri);

        try{

            RFACEBean bean = new RFACEBean(request);
            System.out.println("request.getMethod: " + request.getMethod());
            JSONObject json = new JSONObject();
            json = getGraph(bean);

            json.put("node",bean.getNode());
            return new ModelAndView(new JsonItemsView()).addObject("json",json);
        }catch(Exception e){
            log.warning("exception occurred: " + e);
            return new ModelAndView(new JsonItemsView()).addObject("json", new JSONObject());
        }
    }

    protected JSONObject getGraph(RFACEBean bean){
        JSONObject json = new JSONObject();
         try{
             IndexManager indexMgr = graphDB.index();
             Index<Node> nodeIdx = indexMgr.forNodes("featureIdx");
             Node searchNode = nodeIdx.get("featureid",bean.getNode()).getSingle();

            // traverseGraph(searchNode);
             ArrayList<Node> nodeList = new ArrayList<Node>();
             HashMap<Integer, Node> geneMap = new HashMap<Integer,Node>();
             HashMap<Integer, Relationship> relMap = new HashMap<Integer, Relationship>();
             HashMap<Integer, Integer> countMap = new HashMap<Integer,Integer>();
             HashMap<Integer, Integer> finalGeneMap = new HashMap<Integer, Integer>();
             JSONArray geneArray = new JSONArray();


             System.out.println("node: " + bean.getNode());
             //got all nodes, now sort and go thru the first 200
             
             //get nodes that are related to the search node thru rface values

                 //geneMap.put((Integer)(int)searchNode.getId(),searchNode);
                 finalGeneMap.put((Integer)(int)searchNode.getId(),(Integer)(int)searchNode.getId());
              JSONObject searchJson = new JSONObject();
                    searchJson.put("id", Long.toString(searchNode.getId()));
                    searchJson.put("featureid",(String)searchNode.getProperty("featureid",""));
                    searchJson.put("name",(String)searchNode.getProperty("name",""));
                    searchJson.put("source",(String)searchNode.getProperty("source",""));
                    searchJson.put("label",(String)searchNode.getProperty("name",""));
                    geneArray.put(searchJson);
             MyRelationshipTypes relType = MyRelationshipTypes.RFACE;

            for(Relationship rfaceConnection: searchNode.getRelationships(MyRelationshipTypes.RFACE,Direction.OUTGOING)){
                    if(((Double)rfaceConnection.getProperty("correlation",0) > 0.3 || (Double)rfaceConnection.getProperty("correlation",0) < -0.3)
                            && (Double)rfaceConnection.getProperty("pvalue",10) < 0.01) {
                        relMap.put((Integer)(int)rfaceConnection.getId(),rfaceConnection);
                        nodeList.add(rfaceConnection.getEndNode());
                    }
                }

             for(Node node: nodeList){
                 for(Relationship rfaceConnection: node.getRelationships(MyRelationshipTypes.RFACE,Direction.OUTGOING)){
                      if(((Double)rfaceConnection.getProperty("correlation",0) > 0.3 || (Double)rfaceConnection.getProperty("correlation",0) < -0.3)
                              && (Double)rfaceConnection.getProperty("pvalue",10) < 0.01) {
                        relMap.put((Integer)(int)rfaceConnection.getId(),rfaceConnection);
                          Integer nodeId =   (Integer)(int)node.getId();
                          Integer endNodeId =  (Integer)(int)rfaceConnection.getEndNode().getId();
                          if(countMap.containsKey(nodeId)){
                              countMap.put(nodeId,countMap.get(nodeId)+1);
                          }
                          else{
                              countMap.put(nodeId,1);
                          }
                          if(countMap.containsKey(endNodeId)){
                              countMap.put(endNodeId,countMap.get(endNodeId)+1);
                          }
                          else{
                              countMap.put(endNodeId,1);
                          }
                        geneMap.put((Integer)(int)node.getId(),node);
                        geneMap.put((Integer)(int)rfaceConnection.getEndNode().getId(),rfaceConnection.getEndNode());
                      }
                 }
             }
              nodeList.clear();
             int count=0;
             for(Integer nodeId: countMap.keySet()){
                 if(countMap.get(nodeId) > 1 && count < 175){
                     Node gene = geneMap.get(nodeId);
                    JSONObject geneJson = new JSONObject();
                    geneJson.put("id", Long.toString(gene.getId()));
                    geneJson.put("featureid",(String)gene.getProperty("featureid",""));
                    geneJson.put("name",(String)gene.getProperty("name",""));
                    geneJson.put("source",(String)gene.getProperty("source",""));
                    geneJson.put("label",(String)gene.getProperty("name",""));
                    geneArray.put(geneJson);
                    finalGeneMap.put((Integer)(int)gene.getId(),(Integer)(int)gene.getId());
                     count++;
                 }
             }
             json.put("nodes",geneArray);

             log.info("total nodes: " + geneArray.length());

             JSONArray edgeArray = new JSONArray();

             //now get the edges between all the nodes, need to keep track of the nodes that have already been processed so we don't keep adding their connections
             for(Relationship rel: relMap.values()){
                 if(finalGeneMap.containsKey((Integer)(int)rel.getEndNode().getId()) && finalGeneMap.containsKey((Integer)(int)rel.getStartNode().getId())){
                 JSONObject edgeJson = new JSONObject();
                 edgeJson.put("directed",false);
                 edgeJson.put("id",Long.toString(rel.getId()));
                 edgeJson.put("source",Long.toString(rel.getStartNode().getId()));
                 edgeJson.put("target",Long.toString(rel.getEndNode().getId()));
                 edgeJson.put("label",Long.toString(rel.getId()));
                 edgeJson.put("correlation",(Double)rel.getProperty("correlation",0));
                 edgeJson.put("importance",(Double)rel.getProperty("importance",0));
                 edgeJson.put("pvalue",(Double)rel.getProperty("pvalue",0));
                 edgeArray.put(edgeJson);
                 }
             }

             json.put("edges",edgeArray);
             
            log.info("total edges: " + edgeArray.length());

        }
        catch(Exception e){
           log.info("exception occured: " + e.getMessage());
            return new JSONObject();
        }
        return json;
    }


   /* protected void traverseGraph(Node searchNode){
        final ArrayList<RelationshipType> orderedPathContext = new ArrayList<RelationshipType>();
        orderedPathContext.add(MyRelationshipTypes.NGD);
        orderedPathContext.add(MyRelationshipTypes.DOMINE);
        TraversalDescription td = Traversal.description().evaluator(
                new Evaluator(){
                    private HashMap<String,Node> ngdNodes = new HashMap<String,Node>();

                    @Override
                    public Evaluation evaluate(final Path path){
                        System.out.println("Path length is now: " + path.length());
                        if(path.length() == 0){
                            ngdNodes.put((String)path.endNode().getProperty("name"),path.endNode());
                            return Evaluation.INCLUDE_AND_CONTINUE;
                        }

                                       System.out.println("ngd Nodes size: " + ngdNodes.keySet().size());
                        String currentName = path.lastRelationship().getType().name();
                        String relationshipType = orderedPathContext.get(path.length() -1).name();
                        if(path.length() == orderedPathContext.size()){
                            if(currentName.equals(relationshipType)){
                                //this should be a domine connection.... - ensure end node is in our list
                                 System.out.println("relationshipType: " + relationshipType + " currentName:" + currentName);
                                System.out.println("and at this node: " + (String)path.endNode().getProperty("name"));
                            
                                if(ngdNodes.containsKey((String)path.endNode().getProperty("name"))){
                                    System.out.println("keeping this one " + relationshipType);
                                    return Evaluation.INCLUDE_AND_PRUNE;
                                }
                                return Evaluation.EXCLUDE_AND_PRUNE;
                            }
                            else{
                                return Evaluation.EXCLUDE_AND_PRUNE;
                            }
                        }
                        else{

                            if(currentName.equals(relationshipType)){
                                //this should be the NGD type....
                                System.out.println("ok - adding node to list " + (String)path.endNode().getProperty("name"));

                                ngdNodes.put((String)path.endNode().getProperty("name"),path.endNode());
                                return Evaluation.EXCLUDE_AND_CONTINUE;

                            }
                            else{
                                return Evaluation.EXCLUDE_AND_PRUNE;
                            }
                        }
                    }
                }
        );
        td=td.breadthFirst();
        td=td.relationships(MyRelationshipTypes.NGD,Direction.OUTGOING);
        td=td.relationships(MyRelationshipTypes.DOMINE,Direction.OUTGOING);

        org.neo4j.graphdb.traversal.Traverser t = (org.neo4j.graphdb.traversal.Traverser)td.traverse(searchNode);
        int count=0;
        for(Node n: t.nodes()){
            System.out.println((String)n.getProperty("name"));
            count++;
        }
        System.out.println("total nodes: " + count);
    }
*/
    public void setGraphDB(GraphDatabaseService graphDB){
        this.graphDB=graphDB;
    }

    public GraphDatabaseService getGraphDB(){
        return graphDB;
    }
}
